package com.yang.sofa;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

public class PlaceHolderAnnotationInvocationHandler implements InvocationHandler {
    private final Annotation delegate;
    private final PlaceHolderBinder binder;

    private PlaceHolderAnnotationInvocationHandler(Annotation delegate, PlaceHolderBinder binder) {
        this.delegate = delegate;
        this.binder = binder;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret = method.invoke(this.delegate, args);
        return !ReflectionUtils.isEqualsMethod(method) && !ReflectionUtils.isHashCodeMethod(method) && !ReflectionUtils.isToStringMethod(method) && this.isAttributeMethod(method) ? this.resolvePlaceHolder(ret) : ret;
    }

    private boolean isAttributeMethod(Method method) {
        return method != null && method.getParameterTypes().length == 0 && method.getReturnType() != Void.TYPE;
    }

    public Object resolvePlaceHolder(Object origin) {
        if (!origin.getClass().isArray()) {
            return this.doResolvePlaceHolder(origin);
        } else {
            int length = Array.getLength(origin);
            Object ret = Array.newInstance(origin.getClass().getComponentType(), length);

            for(int i = 0; i < length; ++i) {
                Array.set(ret, i, this.resolvePlaceHolder(Array.get(origin, i)));
            }

            return ret;
        }
    }

    private Object doResolvePlaceHolder(Object origin) {
        if (origin instanceof String) {
            return this.binder.bind((String)origin);
        } else {
            return origin instanceof Annotation && !(origin instanceof WrapperAnnotation) ? PlaceHolderAnnotationInvocationHandler.AnnotationWrapperBuilder.wrap(origin).withBinder(this.binder).build() : origin;
        }
    }

    public static class AnnotationWrapperBuilder<A> {
        private Annotation delegate;
        private PlaceHolderBinder binder;

        private AnnotationWrapperBuilder() {
        }

        public static <A> PlaceHolderAnnotationInvocationHandler.AnnotationWrapperBuilder wrap(A annotation) {
            Assert.isTrue(annotation == null || annotation instanceof Annotation, "Parameter must be annotation type.");
            PlaceHolderAnnotationInvocationHandler.AnnotationWrapperBuilder<A> builder = new PlaceHolderAnnotationInvocationHandler.AnnotationWrapperBuilder();
            builder.delegate = (Annotation)annotation;
            return builder;
        }

        public PlaceHolderAnnotationInvocationHandler.AnnotationWrapperBuilder withBinder(PlaceHolderBinder binder) {
            this.binder = binder;
            return this;
        }

        public A build() {
            if (this.delegate != null) {
                ClassLoader cl = this.getClass().getClassLoader();
                Class<?>[] exposedInterface = new Class[]{this.delegate.annotationType(), WrapperAnnotation.class};
                return Proxy.newProxyInstance(cl, exposedInterface, new PlaceHolderAnnotationInvocationHandler(this.delegate, this.binder));
            } else {
                return null;
            }
        }
    }
}
